package hub

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/websocket"
	"log"
	"strconv"
	"sync"
	"time"
)

//三层控制，CtlCenter控制组

type CtlCenter struct { //控制中心
	rwc         sync.RWMutex
	ClientMap   map[*Group]bool
	CreateGroup chan *Group
	DisGroup    chan *Group
}

//Group 控制每个组里连接，以及群员之间消息的发送与接收
type Group struct {
	*CtlCenter
	rw         sync.RWMutex
	ConnectMap map[*Connection]bool
	GroupId    int
	MsgChan    chan *Message
	Register   chan *Connection
	UnRegister chan *Connection
}

type Connection struct {
	*Group
	connMu sync.RWMutex
	wsConn *websocket.Conn
	User   string
}

type Message struct {
	Time     string `json:"time"`
	UserName string `json:"user_name"`
	Context  string `json:"context"`
}

var Center CtlCenter

func init() {
	Center.ClientMap = make(map[*Group]bool)
	Center.CreateGroup = make(chan *Group)
	Center.DisGroup = make(chan *Group)
	go Center.InitGroupHandle()
}

//InitGroupHandle Ctl处理组的添加与销毁,这个应该在init里被调用
func (ctl *CtlCenter) InitGroupHandle() {
	for {
		select {
		case group := <-Center.CreateGroup: //创一个
			Center.rwc.Lock()
			Center.ClientMap[group] = true
			Center.rwc.Unlock()
		case group := <-Center.DisGroup: //销毁一个
			Center.rwc.Lock()
			delete(Center.ClientMap, group)
			Center.rwc.Unlock()
		}
	}
}

func (g *Group) InitConnHandle() { //处理每个组用户连接的handle，应在创建完一个组后被执行
	for {
		select {
		case conn := <-g.Register:
			conn.rw.Lock()
			conn.ConnectMap[conn] = true
			conn.rw.Unlock()
		case conn := <-g.UnRegister:
			conn.rw.Lock()
			log.Println("删除连接" + strconv.Itoa(conn.GroupId) + ":" + conn.User)
			delete(conn.ConnectMap, conn)
			conn.rw.Unlock()
		}
	}
}

//AddIntoGroup 将用户添加到某个组，有这个组就进入，没有就创建一个新群并进入,应该在创建连接是被调用
func (ctl *CtlCenter) AddIntoGroup(userName string, groupId int, conn *websocket.Conn) *Connection {
	//fmt.Println("添加新组:", groupId)
	exist := false
	for group := range ctl.ClientMap { //有这个组就加入这个组
		if groupId == group.GroupId {
			log.Println("用户" + userName + "进入" + strconv.Itoa(groupId))
			exist = true //这个群组是存在的
			c := &Connection{
				Group:  group,
				wsConn: conn,
				User:   userName,
			}
			group.Register <- c
			return c
		}
	}
	if !exist { //这个组是不存在的情况
		group := &Group{ //先创建一个新组
			CtlCenter:  ctl,
			GroupId:    groupId,
			MsgChan:    make(chan *Message),
			ConnectMap: make(map[*Connection]bool),
			Register:   make(chan *Connection),
			UnRegister: make(chan *Connection),
		}
		log.Println("添加新组:", groupId)
		ctl.CreateGroup <- group
		go group.InitConnHandle() //启动管理这个组连接的协程(初始化组)
		//再将用户添加进组
		c := &Connection{
			Group:  group,
			wsConn: conn,
			User:   userName,
		}
		group.Register <- c
		return c
	}
	return nil //额额
}

func (conn *Connection) Receive() { //接受客户端发送的消息到服务器
	conn.wsConn.SetCloseHandler(func(code int, text string) error {
		log.Println(conn.User + "断开连接")
		return nil
	})
	//conn.wsConn.SetReadDeadline(time.Now().Add(60 * time.Second)) //60s的时间必须有一个来自客户端的消息
	for {
		err := conn.ResetDeadReadTime()
		_, msg, err := conn.wsConn.ReadMessage()
		//if err != nil {
		//	log.Println("conn 超时", conn.User)
		//	panic(err)
		//}
		log.Println("收到消息：", string(msg))
		if err != nil {
			if err == websocket.ErrCloseSent {
				conn.UnRegister <- conn
				log.Println(conn.User + "发生read message错误")
				return
			} else {
				conn.UnRegister <- conn
				//conn.wsConn.Close().Error()
				log.Println(conn.User + "发生read message错误")
				return
			}

		}
		//if msgType == websocket.PongMessage {
		//	log.Println("get a websocket.PongMessage")
		//}
		if string(msg) != "pong" { //不是pong来的消息就把消息放进管道
			var message = Message{
				UserName: conn.User,
				Context:  string(msg),
			}
			//err = json.Unmarshal(msg, &message)
			//if err != nil {
			//	conn.wsConn.Close()
			//	conn.UnRegister <- conn
			//	log.Println(conn.User + "发生Message解析错误")
			//	return
			//}
			conn.MsgChan <- &message //将接收到的用户消息放进群组的msg通道
		} else {
			fmt.Println("收到返回的pong")
			continue
		}
	}
}

func (conn *Connection) Send() { //服务器发送消息到客户端
	//conn.wsConn.SetPongHandler()
	defer log.Println("测试协程是否关闭")
	ticker := time.NewTicker(10 * time.Second)
	for {

		select {
		case msg := <-conn.MsgChan: //广播消息
			msg.Time = time.Now().String()
			marshal, _ := json.Marshal(msg)
			err := conn.Broadcast(marshal, websocket.TextMessage) //父类拥有子类的方法
			if err != nil {
				log.Println(err)
				return
			}
		case <-ticker.C: //ping，pong心跳消息，每30s发送一次，要求10s内写入conn，否则寄
			//conn.mu.Lock()
			//conn.wsConn.SetWriteDeadline(time.Now().Add(10 * time.Second))
			fmt.Println("开始设置ping消息给", conn.User)
			if err := conn.wsConn.WriteMessage(websocket.TextMessage, []byte("ping")); err != nil {
				log.Println("debug conn")
				//conn.mu.Unlock()
				return
			}
			//conn.mu.Unlock()
			fmt.Println("发送完一个ping")
		}
	}
}

func (g *Group) Broadcast(msg []byte, msgType int) (err error) { //对一个组的每一个客户端发送用户消息
	for conn := range g.ConnectMap {
		//conn.connMu.Lock()
		err = conn.wsConn.WriteMessage(msgType, msg)
		//conn.connMu.Unlock()
	}
	return
}

func (conn *Connection) ResetDeadReadTime() (err error) {
	//没有消息就阻塞，到了时间还没通就切断连接，并放行，让后面所有的read操作都会报错
	//在时间内收到消息就放行
	err = conn.wsConn.SetReadDeadline(time.Now().Add(20 * time.Second))
	return err
}
